// QtCore
#include <QtCore/QFile>
#include <QtCore/QDataStream>
#include <QtCore/QTextStream>
// Локальные
#include "math/math.h"
#include "linal/vector.h"

namespace linal
{
    //============================================================================================================================
    //============================================================================================================================
    Vector::Vector(const QString &filename, FileFormat format)
    {
        QFile file(filename);
        QDataStream file_data_stream(&file);
        QTextStream file_stream(&file);

        switch(format)
        {
        case FEM_T:
            file.open(QFile::ReadOnly | QFile::Text);
            while(!file_stream.atEnd())
            {
                //В фале хранятся координаты узла и значения функции. Нам нужно только значение функции.
                double x, y, f;
                file_stream >> x >> y >> f;
                m_Vector.append(f);
            }
            file.close();

            //Последняя строчка в файле пустая.
            m_Vector.remove(m_Vector.size() - 1);
            break;

        case FEM_B:
            file_data_stream.setVersion(QDataStream::Qt_4_7);
            file.open(QFile::ReadOnly);
            while(!file_data_stream.atEnd())
            {
                //В фале хранятся координаты узла и значения функции. Нам нужно только значение функции.
                double x, y, f;
                file_data_stream >> x >> y >> f;
                m_Vector.append(f);
            }
            file.close();
            break;

        case Native:
            file.open(QFile::ReadOnly | QFile::Text);
            int size;
            file_stream >> size;
            m_Vector.resize(size);
            for(int i = 0; i < m_Vector.size(); i++)
                file_stream >> m_Vector[i];
            file.close();
            break;

        default:
            qWarning("Unsupported file format");
        }
    }

    //============================================================================================================================
    //============================================================================================================================
    double Vector::norm() const
    {
        double vector_norm(0.);
        for(int i = 0; i < m_Vector.size(); i++)
            vector_norm += m_Vector[i]*m_Vector[i];
        return qSqrt(vector_norm);
    }

    //============================================================================================================================
    //============================================================================================================================
    double Vector::normSquared() const
    {
        double squared_vector_norm(0.);
        for(int i = 0; i < m_Vector.size(); i++)
            squared_vector_norm += m_Vector[i]*m_Vector[i];
        return squared_vector_norm;
    }

    //============================================================================================================================
    //============================================================================================================================
    double Vector::norm(const Vector& vector)
    {
        double vector_norm(0.);
        for(int i = 0; i < vector.m_Vector.size(); i++)
            vector_norm += vector.m_Vector[i]*vector.m_Vector[i];
        return qSqrt(vector_norm);
    }

    //============================================================================================================================
    //============================================================================================================================
    double Vector::normSquared(const Vector& vector)
    {
        double squared_vector_norm(0.);
        for(int i = 0; i < vector.m_Vector.size(); i++)
            squared_vector_norm += vector.m_Vector[i]*vector.m_Vector[i];
        return squared_vector_norm;
    }

    //============================================================================================================================
    //============================================================================================================================
    void Vector::writeToFile(const QString &filename) const
    {
        QFile file(filename);
        QTextStream file_stream(&file);

        file.open(QFile::WriteOnly | QFile::Text);
        file_stream << m_Vector.size() << endl;
        for(int i = 0; i < m_Vector.size(); i++)
            file_stream << m_Vector[i] << endl;
        file.close();
    }

    //============================================================================================================================
    //============================================================================================================================
    void Vector::readFromFile(const QString &filename, FileFormat format)
    {
        QFile file(filename);
        QDataStream file_data_stream(&file);
        QTextStream file_stream(&file);

        switch(format)
        {
        case FEM_T:
            m_Vector.clear();
            file.open(QFile::ReadOnly | QFile::Text);
            while(!file_stream.atEnd())
            {
                //В фале хранятся координаты узла и значения функции. Нам нужно только значение функции.
                double x, y, f;
                file_stream >> x >> y >> f;
                m_Vector.append(f);
            }
            file.close();

            //Последняя строчка в файле пустая.
            m_Vector.remove(m_Vector.size() - 1);
            break;

        case FEM_B:
            m_Vector.clear();
            file_data_stream.setVersion(QDataStream::Qt_4_7);
            file.open(QFile::ReadOnly);
            while(!file_data_stream.atEnd())
            {
                //В фале хранятся координаты узла и значения функции. Нам нужно только значение функции.
                double x, y, f;
                file_data_stream >> x >> y >> f;
                m_Vector.append(f);
            }
            file.close();
            break;

        case Native:
            file.open(QFile::ReadOnly | QFile::Text);
            int size;
            file_stream >> size;
            m_Vector.resize(size);
            for(int i = 0; i < m_Vector.size(); i++)
                file_stream >> m_Vector[i];
            file.close();
            break;
        default:
            qWarning("Unsupported file format");
        }
    }

    //============================================================================================================================
    //============================================================================================================================
    Vector& Vector::operator+=(const Vector &vector)
    {
        Q_ASSERT_X(m_Vector.size() == vector.m_Vector.size(), "Vector::operator+=", "vectors have different dimensions");
        for(int i = 0; i < m_Vector.size(); i++)
            m_Vector[i] += vector[i];
        return *this;
    }

    Vector& Vector::operator-=(const Vector &vector)
    {
        Q_ASSERT_X(m_Vector.size() == vector.m_Vector.size(), "Vector::operator-=", "vectors have different dimensions");
        for(int i = 0; i < m_Vector.size(); i++)
            m_Vector[i] -= vector[i];
        return *this;
    }

    Vector& Vector::operator*=(double factor)
    {
        for(int i = 0; i < m_Vector.size(); i++)
            m_Vector[i] *= factor;
        return *this;
    }

    Vector& Vector::operator/=(double divisor)
    {
        for(int i = 0; i < m_Vector.size(); i++)
            m_Vector[i] /= divisor;
        return *this;
    }

    //============================================================================================================================
    //============================================================================================================================
    CORE_EXPORT const Vector operator+(const Vector &v1, const Vector &v2)
    {
        Q_ASSERT_X(v1.dimension() == v2.dimension(), "operator+", "vectors have different dimensions");
        Vector v3(v1);
        for(int i = 0; i < v3.dimension(); i++)
            v3[i] += v2[i];
        return v3;
    }

    CORE_EXPORT const Vector operator-(const Vector &v1, const Vector &v2)
    {
        Q_ASSERT_X(v1.dimension() == v2.dimension(), "operator-", "vectors have different dimensions");
        Vector v3(v1);
        for(int i = 0; i < v3.dimension(); i++)
            v3[i] -= v2[i];
        return v3;
    }

    CORE_EXPORT double operator*(const Vector &v1, const Vector &v2)
    {
        Q_ASSERT_X(v1.dimension() == v2.dimension(), "operator*", "vectors have different dimensions");
        double dot_product(0.);
        for(int i = 0; i < v1.dimension(); i++)
            dot_product += v1[i]*v2[i];
        return dot_product;
    }

    CORE_EXPORT const Vector operator*(double factor, const Vector &vector)
    {
        Vector result(vector);
        for(int i = 0; i < result.dimension(); i++)
            result[i] *= factor;
        return result;
    }

    CORE_EXPORT const Vector operator*(const Vector &vector, double factor)
    {
        Vector result(vector);
        for(int i = 0; i < result.dimension(); i++)
            result[i] *= factor;
        return result;
    }

    CORE_EXPORT const Vector operator/(const Vector &vector, double divisor)
    {
        Vector result(vector);
        for(int i = 0; i < result.dimension(); i++)
            result[i] /= divisor;
        return result;
    }

    CORE_EXPORT const Vector operator-(const Vector &vector)
    {
        Vector result(vector);
        for(int i = 0; i < result.dimension(); i++)
            result[i] *= -1.;
        return result;
    }
}
